package com.alex.web.controller;

import com.alex.web.annotation.ExceptionFeedback;
import com.alex.web.bean.CommonResult;
import com.alex.web.bean.member.User;
import com.alex.web.db.domain.PageControl;
import com.alex.web.db.domain.PageInfo;
import com.alex.web.service.UserService;
import com.alex.web.controller.base.BaseController;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Controller
public class LoginController extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private UserService userService;
    @Autowired
    private EhCacheManager ecm;


    @ExceptionFeedback(errorMsg = "登录异常")
    @RequestMapping("/")
    public String index(){

        return "index";

    }


    @ExceptionFeedback(errorMsg = "登录异常")
    @RequestMapping("/login")
    public String login(){

        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        if (user != null){
            return "redirect:";
        }
        return "/login";
    }

    @ExceptionFeedback(errorMsg = "登录异常")
    @RequestMapping("/toLogin")
    @ResponseBody
    public CommonResult toLogin(@RequestParam(value = "username", required = false) String username,
                                @RequestParam(value = "password", required = false) String password,
                                @RequestParam(value = "rme", required = false) boolean rme){
        if(username.isEmpty() || password.isEmpty() ){
            return CommonResult.failResult("请正确输入用户名或密码",null);
        }

        password = DigestUtils.md5Hex(password);
        // 1、 封装用户名、密码、是否记住我到token令牌对象 [支持记住我]
        AuthenticationToken token = new UsernamePasswordToken(username, password, rme);
//            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//            token.setRememberMe(true);
        // 2、 Subject调用login
        Subject subject = SecurityUtils.getSubject();

        try {
            // 3、用户登录
            // 在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查
            // 每个Realm都能在必要时对提交的AuthenticationTokens作出反应
            // 所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法
            subject.login(token);

        } catch (UnknownAccountException uae) {
            logger.error("用户登录，用户验证未通过：未知用户！user=" + username, uae);
            return CommonResult.failResult("该用户不存在",null);
        } catch (IncorrectCredentialsException ice) {
            // 获取输错次数
            logger.error("用户登录，用户验证未通过：错误的凭证，密码输入错误！user=" + username, ice);
            return CommonResult.failResult("用户名或密码不正确",null);
        } catch (LockedAccountException lae) {
            logger.error("用户登录，用户验证未通过：账户已锁定！user=" + username, lae);
            return CommonResult.failResult("账户已锁定",null);
        } catch (ExcessiveAttemptsException eae) {
            // 这里结合了，另一种密码输错限制的实现，基于redis或mysql的实现；也可以直接使用RetryLimitHashedCredentialsMatcher限制5次
            logger.error("用户登录，用户验证未通过：错误次数大于5次,账户已锁定！user=" + username, eae);
            return CommonResult.failResult("输入错误次数大于5次,账户已锁定, 2分钟后可再次登录，或联系管理员解锁",null);
        } catch (AuthenticationException ae) {
            // 通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
            logger.error("用户登录，用户验证未通过：认证异常，异常信息如下！" ,ae);
            return CommonResult.failResult("用户名或密码不正确",null);
        } catch (Exception e) {
            logger.error("用户登录，用户验证未通过：操作异常，异常信息如下！user=" + username, e);
            return CommonResult.failResult("用户登录失败，请您稍后再试",null);
        }
        //获取登录成功的用户对象(以前是直接去service里面查)
        User user = (User) subject.getPrincipal();
        System.out.print(user.toString());

        Cache<String, AtomicInteger> passwordRetryCache = ecm.getCache("passwordRetryCache");
        if (null != passwordRetryCache) {
            int retryNum = (passwordRetryCache.get(user.getMobile()) == null ? 0 : passwordRetryCache.get(user.getMobile())).intValue();
            logger.debug("输错次数：" + retryNum);
            if (retryNum > 0 && retryNum < 6) {
                return CommonResult.failResult("用户名或密码错误" + retryNum + "次,再输错" + (6 - retryNum) + "次账号将锁定",null);
            }
        }

        //将当前登录 的用户放到seesion
//        getSession().setAttribute(SESSION_USER , user);
//        getSession().setAttribute(SESSION_USERID , user.getId());
        return CommonResult.successResult("登录成功",user);

    }
    @ExceptionFeedback(errorMsg = "登出异常")
    @RequestMapping(value="/logout",method= RequestMethod.GET)
    public String logout(){
        //使用权限管理工具进行用户的退出，跳出登录，给出提示信息
        SecurityUtils.getSubject().logout();
        //清除session
        getSession().removeAttribute(SESSION_USER);
        getSession().removeAttribute(SESSION_USERID);
        return "redirect:/login";
    }

    @ExceptionFeedback(errorMsg = "获取用户列表失败")
    @RequestMapping("/user/userList")
    public String userList(Model model,int page){
        if(page == 0){
            page = 1;
        }
        User entity = new User();
        PageInfo info = new PageInfo(page>0?page-1:0);
        PageControl<User> control = userService.page(entity,info,null,null);
        model.addAttribute("page",control);
        return "/member/user/list";
    }

    @ExceptionFeedback(errorMsg = "获取用户列表失败")
    @RequestMapping("/add")
    public String addUser(){
        return "/member/user/list";
    }
}
